package org.dromara.common.core.utils;

import cn.hutool.extra.spring.SpringUtil;
import lombok.extern.slf4j.Slf4j;
import org.dromara.common.core.exception.InvalidUsageException;
import org.springframework.aop.framework.AopContext;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;

import java.lang.annotation.Annotation;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * spring工具类
 *
 * @author fly
 */
@Slf4j
@Component
public final class SpringUtils extends SpringUtil {

    /**
     * 如果BeanFactory包含一个与所给名称匹配的bean定义，则返回true
     */
    public static boolean containsBean(String name) {
        return getBeanFactory().containsBean(name);
    }

    /**
     * 判断以给定名字注册的bean定义是一个singleton还是一个prototype。
     * 如果与给定名字相应的bean定义没有被找到，将会抛出一个异常（NoSuchBeanDefinitionException）
     */
    public static boolean isSingleton(String name) throws NoSuchBeanDefinitionException {
        return getBeanFactory().isSingleton(name);
    }

    /**
     * @return Class 注册对象的类型
     */
    public static Class<?> getType(String name) throws NoSuchBeanDefinitionException {
        return getBeanFactory().getType(name);
    }

    /**
     * 如果给定的bean名字在bean定义中有别名，则返回这些别名
     */
    public static String[] getAliases(String name) throws NoSuchBeanDefinitionException {
        return getBeanFactory().getAliases(name);
    }

    /**
     * 获取指定类型的单个Bean实例
     *
     * @param clazz
     * @return
     */
    public static <T> T getBean(Class<T> clazz) {
        List<T> clazzInstances = getBeans(clazz);
        if (clazzInstances == null) {
            log.debug("instance not found: {}", clazz.getSimpleName());
            return null;
        }
        if (clazzInstances.size() > 1) {
            throw new InvalidUsageException("getBean({}.class) 识别到多个实例，请检查调用！", clazz.getSimpleName());
        }
        return clazzInstances.get(0);
    }

    /**
     * 获取指定类型的全部实现类
     *
     * @param type
     * @param <T>
     * @return
     */
    public static <T> List<T> getBeans(Class<T> type) {
        Map<String, T> map = getApplicationContext().getBeansOfType(type);
        if (CollectionUtils.isEmpty(map)) {
            return null;
        }
        List<T> beanList = new ArrayList<>(map.size());
        beanList.addAll(map.values());
        return beanList;
    }

    /**
     * 根据注解获取beans
     *
     * @param annotationType
     * @return
     */
    public static List<Object> getBeansByAnnotation(Class<? extends Annotation> annotationType) {
        Map<String, Object> map = getApplicationContext().getBeansWithAnnotation(annotationType);
        if (CollectionUtils.isEmpty(map)) {
            return null;
        }
        return new ArrayList<>(map.values());
    }

    /**
     * 根据注解获取beans
     *
     * @param annotationType
     * @return
     */
    public static Map<String, Object> getBeansMapByAnnotation(Class<? extends Annotation> annotationType) {
        Map<String, Object> map = getApplicationContext().getBeansWithAnnotation(annotationType);
        if (CollectionUtils.isEmpty(map)) {
            return null;
        }
        return map;
    }


    /**
     * 获取aop代理对象
     */
    @SuppressWarnings("unchecked")
    public static <T> T getAopProxy(T invoker) {
        return (T) AopContext.currentProxy();
    }


    /**
     * 获取spring上下文
     */
    public static ApplicationContext context() {
        return getApplicationContext();
    }

}
